class_name UnitMover
extends Node

## 单位移动时高亮可被放置的区域
@export var tile_highlighter: TileHighlighter

##
@export var init_play_areas: Array[PlayArea]

##
var _transitions: Dictionary

##
var _play_area_map: Dictionary

##
var _is_dragging: bool = false


func _ready() -> void:
	for play_area: PlayArea in init_play_areas:
		register_play_area(play_area)


func register_play_area(play_area: PlayArea) -> void:
	_play_area_map[play_area.name] = play_area


func add_transition(source_play_area_name: String, movement_transition: MovementTransition) -> void:
	_transitions.get_or_add(source_play_area_name, []).append(movement_transition)


func setup_unit(unit: Variant) -> void:
	unit.drag_and_drop.drag_started.connect(_on_unit_drag_started.bind(unit))
	unit.drag_and_drop.drag_canceled.connect(_on_unit_drag_canceled.bind(unit))
	unit.drag_and_drop.dropped.connect(_on_unit_dropped.bind(unit))


func _process(_delta: float) -> void:
	if _is_dragging and _play_area_map.size() > 0:
		var mouse_global_position: Vector2 = _play_area_map.values()[0].get_global_mouse_position()
		var mouse_hovered_play_area := _get_play_area_by_global_position(mouse_global_position)
		if mouse_hovered_play_area:
			tile_highlighter.clear_highlighted_tiles()
			tile_highlighter.highlight_tile_by_global_position(mouse_global_position)


func _get_first_available_play_area() -> PlayArea:
	for play_area: PlayArea in _play_area_map.values():
		if play_area.unit_grid.has_empty_cell():
			return play_area
	return null


func _get_play_area_by_global_position(global_position: Vector2) -> PlayArea:
	for play_area: PlayArea in _play_area_map.values():
		var cell_position := play_area.get_cell_coord_by_global_position(global_position)
		if play_area.unit_grid.is_cell_in_grid(cell_position):
			return play_area
	return null


func _reset_unit_to_global_position(unit: Variant, global_position: Vector2) -> void:
	var play_area := _get_play_area_by_global_position(global_position)
	if not play_area:
		Logger.fatal(
			"No play area contains this global position.",
			{"global_position": global_position}
		)
	
	var cell_coord := play_area.get_cell_coord_by_global_position(global_position)
	
	# 在返回原始位置时，也需要考虑位置是或否被占用了
	if not play_area.unit_grid.is_cell_empty(cell_coord):
		# 原先位置被占用，寻找新的空余位置
		play_area = _get_first_available_play_area()
		if play_area:
			cell_coord = play_area.unit_grid.get_first_empty_cell_coord()
		else:
			Logger.fatal("No more empty cells to set this unit.")
	
	if not play_area.add_unit_to_empty_cell(unit, cell_coord):
		Logger.fatal(
			"Can not set unit to this coord.",
			{"cell_coord": cell_coord}
		)


func _on_unit_drag_started(unit: Variant) -> void:
	_is_dragging = true
	tile_highlighter.active = true
	
	var play_area := _get_play_area_by_global_position(unit.global_position)
	if not play_area:
		Logger.fatal("No play area contains this global position.")
	
	play_area.remove_unit(unit)
	add_child(unit)


func _on_unit_drag_canceled(start_global_position: Vector2, unit: Variant) -> void:
	_is_dragging = false
	tile_highlighter.active = true
	
	_reset_unit_to_global_position(unit, start_global_position)


func _on_unit_dropped(start_global_position: Vector2, unit: Variant) -> void:
	_is_dragging = false
	tile_highlighter.active = true
	
	var old_area := _get_play_area_by_global_position(start_global_position)
	if not old_area:
		Logger.fatal(
			"No play area contains this global position.",
			{"global_position": start_global_position}
		)
	
	var old_cell_coord := old_area.get_cell_coord_by_global_position(start_global_position)
	
	var mouse_global_position: Vector2 = unit.get_global_mouse_position()
	var new_area := _get_play_area_by_global_position(mouse_global_position)

	var test_transitions: Callable = func(): 
		for transition: MovementTransition in _transitions.get(old_area.name):
			if transition.try_condition() and transition.try_transition() == new_area.name:
				return true
		return false

	# 拖拽到无效区域了，返回原始位置
	if not new_area or not test_transitions.call():
		_reset_unit_to_global_position(unit, start_global_position)
		return
	
	var new_cell_coord := new_area.get_cell_coord_by_global_position(mouse_global_position)
	
	# 如果释放拖拽的单元格不为空单元格
	if not new_area.add_unit_to_empty_cell(unit, new_cell_coord):
		var orignal_unit = new_area.unit_grid.get_unit(new_cell_coord)
		
		# 替换对象
		if not new_area.remove_unit_by_cell_coord(new_cell_coord):
			Logger.fatal(
				"Unable to remove unit from global position.",
				{"global_position": mouse_global_position}
			)
		
		if not old_area.add_unit_to_empty_cell(orignal_unit, old_cell_coord):
			Logger.fatal(
				"Unable to set unit from global position.",
				{"global_position": start_global_position}
			)

		if not new_area.add_unit_to_empty_cell(unit, new_cell_coord):
			Logger.fatal(
				"Unable to set unit from global position.",
				{"global_position": mouse_global_position}
			)
